package distributed

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/louloulin/dataflare/pkg/cluster/clusterapi"
	"github.com/louloulin/dataflare/pkg/workflow"
)

// NewEngine creates a new distributed engine
func NewEngine(clusterManager clusterapi.Manager) (*Engine, error) {
	// Create local engine
	localEngine, err := workflow.NewEngine()
	if err != nil {
		return nil, err
	}

	// Create adapter
	adapter := &Engine{
		clusterManager: clusterManager,
		localEngine:    localEngine,
		workflows:      make(map[string]*workflow.Definition),
		runs:           make(map[string]*workflow.Run),
	}

	return adapter, nil
}

// Engine is an adapter for the distributed engine
type Engine struct {
	// clusterManager is the cluster manager
	clusterManager clusterapi.Manager
	// localEngine is the local workflow engine
	localEngine *workflow.Engine
	// workflows is a map of workflow IDs to workflow definitions
	workflows map[string]*workflow.Definition
	// runs is a map of run IDs to workflow runs
	runs map[string]*workflow.Run
	// mu is a mutex for protecting shared state
	mu sync.RWMutex
}

// Shutdown shuts down the engine
func (e *Engine) Shutdown() {
	e.localEngine.Shutdown()
}

// DeployWorkflow deploys a workflow
func (e *Engine) DeployWorkflow(ctx context.Context, definition *workflow.Definition) (string, error) {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Generate workflow ID if not provided
	if definition.ID == "" {
		definition.ID = uuid.New().String()
	}

	// Store workflow definition
	e.workflows[definition.ID] = definition

	// Deploy workflow to local engine
	_, err := e.localEngine.DeployWorkflow(ctx, definition)
	if err != nil {
		return "", err
	}

	return definition.ID, nil
}

// StartWorkflow starts a workflow
func (e *Engine) StartWorkflow(ctx context.Context, workflowID string, input map[string]interface{}) (string, error) {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Check if workflow exists
	if _, ok := e.workflows[workflowID]; !ok {
		return "", fmt.Errorf("workflow not found: %s", workflowID)
	}

	// Generate run ID
	runID := uuid.New().String()

	// Create run
	run := &workflow.Run{
		ID:         runID,
		WorkflowID: workflowID,
		Status:     "running",
		Input:      input,
		StartTime:  time.Now(),
	}

	// Store run
	e.runs[runID] = run

	// Start workflow on local engine
	_, err := e.localEngine.StartWorkflow(ctx, workflowID, input)
	if err != nil {
		return "", err
	}

	return runID, nil
}

// StopWorkflow stops a workflow
func (e *Engine) StopWorkflow(ctx context.Context, runID string) error {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Check if run exists
	run, ok := e.runs[runID]
	if !ok {
		return fmt.Errorf("run not found: %s", runID)
	}

	// Update run status
	run.Status = "stopped"
	run.EndTime = time.Now()

	// Stop workflow on local engine
	err := e.localEngine.StopWorkflow(ctx, runID)
	if err != nil {
		return err
	}

	return nil
}

// GetWorkflowStatus gets the status of a workflow
func (e *Engine) GetWorkflowStatus(ctx context.Context, runID string) (*workflow.RunStatus, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()

	// Check if run exists
	run, ok := e.runs[runID]
	if !ok {
		return nil, fmt.Errorf("run not found: %s", runID)
	}

	// Create status
	status := &workflow.RunStatus{
		RunID:      runID,
		WorkflowID: run.WorkflowID,
		Status:     run.Status,
		Progress:   100,
		StartTime:  run.StartTime,
		EndTime:    run.EndTime,
	}

	return status, nil
}
